/*******************************************************************/
/* @ All Rights Reserved Copyright (C) 2014 broada,Co.,Ltd.        */
/*                                                                 */
/*  NAME      = frshm_stat.c                                       */
/*  NOTE      =                                                    */
/*  DATE      = 2016/02/19 by guojq                                */
/*******************************************************************/
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>

#include <sys/shm.h>

#include "frcomm.h"
#include "frmem.h"
#include "frlog.h"
#include "frsem.h"
#include "frshm_pool.h"
#include "frshm_stat.h"

//共享内存统计信息最大统计个数
#define FRSHM_SHM_MAX   20
#define FRSHM_NAME_MAX  40

typedef struct
{
    int i_used;     //是否被使用
    char arr_name[FRSHM_NAME_MAX]; //内存池名称
    int i_shmid;    //共享内存池的id
    int i_count;    //当前使用的共享内存块的个数
    int i_max;      //最大共享内存块个数
    int i_memused;  //真正使用的内存
    int i_count_all;//历史使用内存块总数
    int i_over;     //历史超过预分配大小的个数
} Frshmstat;

typedef struct
{
    int i_count; //当前使用的共享内存池个数
    Frshmstat st_shm_stat[FRSHM_SHM_MAX];
} Frshmstat_mng;

//统计共享内存使用的id
int g_i_shm_stat_id = -1;
//统计共享内存使用的进程锁
int g_i_sem_stat_id = -1;

int frshm_stat_init(void)
{
    int i_key = 0;
    FILE *fp = NULL;
    int i_shmid = 0, i_semid = 0;
    
    //初始化统计信息的共享内存文件
    fp = fopen(FRSHM_STAT_FILE, "a");
    if (fp == NULL)
    {
        FRLOG_ERROR_DETAIL("[%s] create failed\n", FRSHM_STAT_FILE);
        return FR_FAIL;
    }
    fclose(fp);
    
    //初始化统计信息的进程锁文件
    fp = fopen(FRSEM_STAT_FILE, "a");
    if (fp == NULL)
    {
        FRLOG_ERROR_DETAIL("[%s] create failed\n", FRSEM_STAT_FILE);
        return FR_FAIL;
    }
    fclose(fp);
    
    i_key = ftok(FRSHM_STAT_FILE, 1);
    if (i_key < 0)
        return FR_FAIL;
    
    //初始化统计信息的共享内存
    i_shmid = shmget((key_t)i_key, sizeof(Frshmstat_mng), IPC_CREAT|IPC_EXCL);
    if(i_shmid < 0)
        return FR_FAIL;
    
    //初始化统计信息的进程锁
    i_semid = frsem_create(FRSEM_STAT_FILE);
    if (i_semid < 0)
    {
        shmctl(i_shmid, IPC_RMID, 0);
        return FR_FAIL;
    }
    
    return FR_SUCC;
}

int frshm_stat_get(void)
{
    g_i_shm_stat_id = frshm_pool_get(FRSHM_STAT_FILE);
    if (g_i_shm_stat_id < 0)
        return FR_FAIL;
    g_i_sem_stat_id = frsem_get(FRSEM_STAT_FILE);
    if (g_i_sem_stat_id < 0)
        return FR_FAIL;
    return FR_SUCC;
}

/*******************************************************************************
*  FUNC     :  新建一个共享内存池的统计信息
*  ARGS     :  i_shmid                  统计信息的共享内存ID
*           :  i_new_shmid              加入的共享内存池ID
*           :  p_name                   加入的共享内存池名称
*           :  i_pool_size              加入的共享内存池链表总个数
*           :  i_shm_size               加入的共享内存池单个内存块大小
*           :  p_i_index                统计信息的存储序号
*  RTN      :  FR_SUCC              
*              FR_FAIL              
*  NOTE     :  
*******************************************************************************/
FR_STATIC int __frshm_stat_new(int i_shmid, int i_new_shmid, char *p_name
                   , int i_pool_size, int i_shm_size
                   , int *p_i_index)
{
    void *p_shm = NULL;
    Frshmstat_mng *p_shm_stat = NULL;
    int i_loop = 0;
    
    if (i_shmid < 0 || i_new_shmid < 0 || p_name == NULL || p_i_index == NULL)
        return FR_FAIL;
    
    p_shm = shmat(i_shmid, NULL, 0);
    if (p_shm == (void*)-1)
    {
        shmctl(i_shmid, IPC_RMID, 0);
        return FR_FAIL;
    }
    
    p_shm_stat = (Frshmstat_mng *)p_shm;
    
    if (p_shm_stat->i_count + 1 >= FRSHM_SHM_MAX)
    {
        FRLOG_WARN("too many share memory");
        shmdt(p_shm);
        return FR_FAIL;
    }
    
    for (i_loop=0;i_loop<FRSHM_SHM_MAX;i_loop++)
    {
        if (p_shm_stat->st_shm_stat[i_loop].i_used == 0)
            break;
    }
    
    memset((void *)&p_shm_stat->st_shm_stat[i_loop], 0x00, sizeof(Frshmstat));
    p_shm_stat->st_shm_stat[i_loop].i_shmid = i_new_shmid;
    p_shm_stat->st_shm_stat[i_loop].i_max = i_pool_size;
    p_shm_stat->st_shm_stat[i_loop].i_used = 1;
    if (p_name)
        snprintf(p_shm_stat->st_shm_stat[i_loop].arr_name
               , sizeof(p_shm_stat->st_shm_stat[i_loop].arr_name)
               , "%s", p_name);
    p_shm_stat->i_count++;
    *p_i_index = i_loop;
    shmdt(p_shm);
    
    return FR_SUCC;
}

/*******************************************************************************
*  FUNC     :  统计添加信息
*  ARGS     :  i_shmid                  统计信息的共享内存ID
*           :  i_index                  统计信息的存储序号
*           :  i_data_len               添加的内存大小
*           :  i_flag_ismax             是否为最大值的内存 1:最大值 0:非最大
*  RTN      :  FR_SUCC              
*              FR_FAIL              
*  NOTE     :  
*******************************************************************************/
FR_STATIC int __frshm_stat_add(int i_shmid, int i_index, int i_data_len, int i_flag_ismax)
{
    void *p_shm = NULL;
    Frshmstat_mng *p_shm_stat = NULL;
    
    if (i_shmid < 0 || i_index < 0 || i_index >= FRSHM_SHM_MAX)
        return FR_FAIL;
    p_shm = shmat(i_shmid, NULL, 0);
    if (p_shm == (void*)-1)
    {
        shmctl(i_shmid, IPC_RMID, 0);
        return FR_FAIL;
    }
    
    p_shm_stat = (Frshmstat_mng *)p_shm;
    
    p_shm_stat->st_shm_stat[i_index].i_count++;
    p_shm_stat->st_shm_stat[i_index].i_count_all++;
    p_shm_stat->st_shm_stat[i_index].i_memused += i_data_len;
    if (i_flag_ismax)
        p_shm_stat->st_shm_stat[i_index].i_over += 1;
    shmdt(p_shm);
    return FR_SUCC;
}

/*******************************************************************************
*  FUNC     :  统计删除信息
*  ARGS     :  i_shmid                  统计信息的共享内存ID
*           :  i_index                  统计信息的存储序号
*           :  i_data_len               删除的内存大小
*  RTN      :  FR_SUCC              
*              FR_FAIL              
*  NOTE     :  
*******************************************************************************/
FR_STATIC int __frshm_stat_sub(int i_shmid, int i_index, int i_data_len)
{
    void *p_shm = NULL;
    Frshmstat_mng *p_shm_stat = NULL;
    
    if (i_shmid < 0 || i_index < 0 || i_index >= FRSHM_SHM_MAX)
        return FR_FAIL;
    
    p_shm = shmat(i_shmid, NULL, 0);
    if (p_shm == (void*)-1)
    {
        shmctl(i_shmid, IPC_RMID, 0);
        return FR_FAIL;
    }
    
    p_shm_stat = (Frshmstat_mng *)p_shm;
    p_shm_stat->st_shm_stat[i_index].i_count--;
    p_shm_stat->st_shm_stat[i_index].i_memused -= i_data_len;
    shmdt(p_shm);
    return FR_SUCC;
}

/*******************************************************************************
*  FUNC     :  删除一个内存池的统计信息
*  ARGS     :  i_shmid                  统计信息的共享内存ID
*           :  i_index                  统计信息的存储序号
*  RTN      :  FR_SUCC              
*              FR_FAIL              
*  NOTE     :  
*******************************************************************************/
FR_STATIC int __frshm_stat_free(int i_shmid, int i_index)
{
    void *p_shm = NULL;
    Frshmstat_mng *p_shm_stat = NULL;
    
    if (i_shmid < 0 || i_index < 0 || i_index >= FRSHM_SHM_MAX)
        return FR_FAIL;
    
    p_shm = shmat(i_shmid, NULL, 0);
    if (p_shm == (void*)-1)
    {
        shmctl(i_shmid, IPC_RMID, 0);
        return FR_FAIL;
    }
    
    p_shm_stat = (Frshmstat_mng *)p_shm;
    memset((void *)&p_shm_stat->st_shm_stat[i_index], 0x00, sizeof(Frshmstat));
    shmdt(p_shm);
    return FR_SUCC;
}

/*******************************************************************************
*  FUNC     :  输出所有共享内存统计信息
*  ARGS     :  i_shmid                  统计信息的共享内存ID
*           :  pp_out                   所有共享内存统计信息的内容
*  RTN      :  FR_SUCC              
*              FR_FAIL              
*  NOTE     :  
*******************************************************************************/
FR_STATIC int __frshm_stat_info(int i_shmid, char **pp_out)
{
    char *p_info = NULL;
    char arr_shm_node[128];
    int i_len = 3072;
    void *p_shm = NULL;
    Frshmstat_mng *p_shm_stat = NULL;
    int i = 0;
    int i_rate = 0;
    
    if (i_shmid < 0)
        return FR_FAIL;
    
    p_shm = shmat(i_shmid, NULL, 0);
    if (p_shm == (void*)-1)
    {
        shmctl(i_shmid, IPC_RMID, 0);
        return FR_FAIL;
    }
    
    p_shm_stat = (Frshmstat_mng *)p_shm;
    
    p_info = (char *)frmalloc(i_len);
    if (p_info == NULL)
        goto err;
    
    p_info[0] = '\0';
    if (p_shm_stat->i_count == 0)
    {
        strcpy(p_info, "share memory is NULL\n");
        goto succ;
    }
    strcpy(p_info, "共享内存id(shmid) 程序(name) 已使用率(used) 已使用内存(memory) 溢出总个数(overflow) 使用总个数(all) 溢出率(rate)\n");
    
    for (i=0;i<FRSHM_SHM_MAX;i++)
    {
        if (p_shm_stat->st_shm_stat[i].i_used)
        {
            if (p_shm_stat->st_shm_stat[i].i_count_all == 0)
                i_rate = 0;
            else
                i_rate = p_shm_stat->st_shm_stat[i].i_over*100/p_shm_stat->st_shm_stat[i].i_count_all;

            snprintf(arr_shm_node, sizeof(arr_shm_node)
                   , "shmid:%d  name=[%s]  used:%d/%d  memory:%dM(%dbyte)  overflow:%d  all:%d  rate:%d%%\n"
                   , p_shm_stat->st_shm_stat[i].i_shmid
                   , p_shm_stat->st_shm_stat[i].arr_name
                   , p_shm_stat->st_shm_stat[i].i_count
                   , p_shm_stat->st_shm_stat[i].i_max
                   , p_shm_stat->st_shm_stat[i].i_memused/1024
                   , p_shm_stat->st_shm_stat[i].i_memused
                   , p_shm_stat->st_shm_stat[i].i_over
                   , p_shm_stat->st_shm_stat[i].i_count_all
                   , i_rate);
            strcat(p_info, arr_shm_node);
        }
    }
succ:
    *pp_out = p_info;
    shmdt(p_shm);
    return FR_SUCC;
err:
    if (p_info)
        frfree(p_info);
    shmdt(p_shm);
    return FR_FAIL;
}

/*******************************************************************************
*  FUNC     :  退出共享内存统计
*  ARGS     :  i_shmid                  统计信息的共享内存ID
*  RTN      :                
*  NOTE     :  
*******************************************************************************/
FR_STATIC void __frshm_stat_exit(int i_shmid)
{
    shmctl(i_shmid, IPC_RMID, 0);
}

int frshm_stat_new2(int i_shmid, int i_semid
                 , int i_new_shmid, char *p_name, int i_pool_size
                 , int i_shm_size, int *p_i_index)
{
    int i_ret = 0;
    frsemaphore_lock(i_semid);
    i_ret = __frshm_stat_new(i_shmid, i_new_shmid, p_name, i_pool_size, i_shm_size, p_i_index);
    frsemaphore_unlock(i_semid);
    return i_ret;
}

int frshm_stat_new(int i_new_shmid, char *p_name, int i_pool_size
                 , int i_shm_size, int *p_i_index)
{
    int i_ret = 0;
    int i_shmid = 0, i_semid = 0;
    
    i_shmid = frshm_pool_get(FRSHM_STAT_FILE);
    if (i_shmid < 0)
    {
        if (frshm_stat_init() != FR_SUCC)
            return FR_FAIL;
        i_shmid = frshm_pool_get(FRSHM_STAT_FILE);
        if (i_shmid < 0)
            return FR_FAIL;
    }
    
    i_semid = frsem_get(FRSEM_STAT_FILE);
    if (i_semid < 0)
        return FR_FAIL;
    frsemaphore_lock(i_semid);
    i_ret = __frshm_stat_new(i_shmid, i_new_shmid, p_name, i_pool_size, i_shm_size, p_i_index);
    frsemaphore_unlock(i_semid);
    return i_ret;
}

int frshm_stat_add(int i_shmid, int i_semid
                 , int i_index, int i_data_len
                 , int i_flag_ismax)
{
    int i_ret = 0;
    frsemaphore_lock(i_semid);
    i_ret = __frshm_stat_add(i_shmid, i_index, i_data_len, i_flag_ismax);
    frsemaphore_unlock(i_semid);
    return i_ret;
}

int frshm_stat_sub(int i_shmid, int i_semid
                 , int i_index, int i_data_len)
{
    int i_ret = 0;
    frsemaphore_lock(i_semid);
    i_ret = __frshm_stat_sub(i_shmid, i_index, i_data_len);
    frsemaphore_unlock(i_semid);
    return i_ret;
}

int frshm_stat_free(int i_shmid, int i_semid, int i_index)
{
    int i_ret = 0;
    frsemaphore_lock(i_semid);
    i_ret = __frshm_stat_free(i_shmid, i_index);
    frsemaphore_unlock(i_semid);
    return i_ret;
}

int frshm_stat_info2(int i_shmid, int i_semid, char **pp_out)
{
    int i_ret = 0;
    frsemaphore_lock(i_semid);
    i_ret = __frshm_stat_info(i_shmid, pp_out);
    frsemaphore_unlock(i_semid);
    return i_ret;
}

int frshm_stat_info(char **pp_out)
{
    int i_ret = 0;
    int i_shmid = 0, i_semid = 0;
    
    i_shmid = frshm_pool_get(FRSHM_STAT_FILE);
    if (i_shmid < 0)
        return FR_FAIL;
    
    i_semid = frsem_get(FRSEM_STAT_FILE);
    if (i_semid < 0)
        return FR_FAIL;
        
    frsemaphore_lock(i_semid);
    i_ret = __frshm_stat_info(i_shmid, pp_out);
    frsemaphore_unlock(i_semid);
    return i_ret;
}

int frshm_stat_exit2(int i_shmid, int i_semid)
{
    frsemaphore_lock(i_semid);
    __frshm_stat_exit(i_shmid);
    frsemaphore_unlock(i_semid);
    frsem_free(i_semid);
    
    return FR_SUCC;
}

int frshm_stat_exit(void)
{
    int i_shmid = 0, i_semid = 0;
    
    i_shmid = frshm_pool_get(FRSHM_STAT_FILE);
    if (i_shmid < 0)
        return FR_FAIL;
    
    i_semid = frsem_get(FRSEM_STAT_FILE);
    if (i_semid < 0)
        return FR_FAIL;
    
    frsemaphore_lock(i_semid);
    __frshm_stat_exit(i_shmid);
    frsemaphore_unlock(i_semid);
    frsem_free(i_semid);
    
    return FR_SUCC;
}
